/**
 * @file lv_i18n_lang.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_i18n_lang.h"

#if LV_USE_MULTI_I18N_LANG

#include "../lvgl/src/lv_misc/lv_gc.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static const char* lang_get_i18n_locale_name(const uint8_t lang_id);
static uint8_t lang_get_i18n_language_pack_index(const char* locale_name);
static const void* lang_get_txt(const uint16_t txt_id);
static void lang_set_core(lv_obj_t * obj);
static lv_res_t lv_lang_label_sign_cb(struct _lv_obj_t * obj, lv_signal_t sign, void * param);

/**********************
 *  STATIC VARIABLES
 **********************/

static const lv_i18n_lang_locale_t lang_locale_list[] = {
    {UI_LANG_EN,  "en-GB"/* English */},
    {UI_LANG_CH,  "zh-CN"/* Chinese */}
};

static uint8_t lang_act = 0;
static const uint8_t lang_act_num_max = sizeof(lang_locale_list)/sizeof(lv_i18n_lang_locale_t);


/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/
/**
 * Change the language
 * @param lang_id the id of the language, defined as "LANG_EN/LANG_CH/LANG_AR"
 */
void lv_lang_set(const uint8_t lang_id)
{
    lang_act = lang_id;
    const char* locale_name = lang_get_i18n_locale_name(lang_id);
    const uint8_t pack_index = lang_get_i18n_language_pack_index(locale_name);

    if(pack_index < lv_i18n_language_num_max){
        __lv_i18n_reset();
        lv_i18n_init(&lv_i18n_language_pack[pack_index]);
    }

    lv_i18n_set_locale(locale_name);

    lv_disp_t * d;
    _LV_LL_READ(LV_GC_ROOT(_lv_disp_ll), d) {
        lv_obj_t * s;
        _LV_LL_READ(d->scr_ll, s) {
            lv_signal_send(s, LV_SIGNAL_LANG_CHG, NULL);
            lang_set_core(s);
        }
    }

    lang_set_core(lv_disp_get_scr_act(NULL));
}


/**
 * Get the `txt_id` text in the set language
 * @param txt_id an ID of the text to get
 * @return the `txt_id` txt on the set language
 */
const void * lv_lang_get_text(const uint16_t txt_id)
{
    if(txt_id == LV_LANG_TXT_ID_NONE) {
        LV_LOG_WARN("lv_lang_get_text: attempts to get invalid text ID");
        return NULL;      /*Invalid txt_id*/
    }

    return lang_get_txt(txt_id);
}


/**
 * Return with ID of the currently selected language
 * @return pointer to the active screen object (loaded by 'lv_scr_load()')
 */
uint8_t lv_lang_act(void)
{
    return lang_act;
}


/**
 *Set a text ID which refers a the same text but in a different languages
 * @param label pointer to a label object
 * @param txt_id ID of the text
 */
void lv_label_set_text_id(lv_obj_t * label, const uint32_t txt_id)
{
    label->user_data.user_num = txt_id;                       //save text id

    if(lv_lang_label_sign_cb != lv_obj_get_signal_cb(label)) {
        lv_label_ext_t * ext = (lv_label_ext_t *)lv_obj_get_ext_attr(label);
        ext->old_sign_cb = lv_obj_get_signal_cb(label);        //save label signal cb
        lv_obj_set_signal_cb(label, lv_lang_label_sign_cb);     //set new signal cb
    }

    /*Apply the new language*/
    lv_signal_send(label, LV_SIGNAL_LANG_CHG, NULL);
}

/**
 * Get a text ID
 * @param label pointer to a label object
 * @return ID of the text
 */
uint32_t lv_label_get_text_id(lv_obj_t * label)
{
    return label->user_data.user_num;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
/**
 * Get the i18n locale name by language id
 * @param lang_id ID, defined as "LANG_EN/LANG_CH/LANG_AR"
 */
static const char* lang_get_i18n_locale_name(const uint8_t lang_id)
{
    const char* locale_name = "en-GB";
    uint8_t i;
    for(i = 0; i < lang_act_num_max; i++){
        if(lang_locale_list[i].lang_id == lang_id){
            locale_name = lang_locale_list[i].locale_name;
            break;
        }
    }

    return locale_name;
}

/**
 * Get the i18n language pack index by locale name
 * @param locale_name, defined as "en-GB/zh-CN/AR"
 */
static uint8_t lang_get_i18n_language_pack_index(const char* locale_name)
{
    uint8_t pack_index = 0;
    uint8_t i;
    for(i = 0; i < lv_i18n_language_num_max; i++){
        if(strcmp(lv_i18n_language_pack[i]->locale_name, locale_name) == 0){
            pack_index = i;
            break;
        }
    }

    return pack_index;
}

/**
 * Get the language text by text id
 * @param txt_id ID of the text
 */
static const void* lang_get_txt(const uint16_t txt_id)
{
    const char* txt = NULL;
    if(txt_id < LV_I18N_MSG_ID_MAX) {
        txt = lv_i18n_get_text(txt_id);
    }

    return txt;
}

/**
 * Change the language of the children. (Called recursively)
 * @param obj pointer to an object
 */
static void lang_set_core(lv_obj_t * obj)
{
    lv_obj_t * i;
    _LV_LL_READ(obj->child_ll, i) {
        lv_signal_send(i, LV_SIGNAL_LANG_CHG, NULL);

        lang_set_core(i);
    }
}

static lv_res_t lv_lang_label_sign_cb(struct _lv_obj_t * obj, lv_signal_t sign, void * param)
{
    lv_label_ext_t * ext = (lv_label_ext_t *)lv_obj_get_ext_attr(obj);
    if(ext->old_sign_cb) ext->old_sign_cb(obj, sign, param);

    uint32_t textid = obj->user_data.user_num;
    if(LV_SIGNAL_LANG_CHG == sign) {
        if(textid != LV_LANG_TXT_ID_NONE) {
            const char * lang_txt = lv_lang_get_text(textid);
            if(lang_txt) {
                lv_label_set_text(obj, lang_txt);
            } else {
                LV_LOG_WARN("lv_lang_get_text return NULL for a label's text");
            }
        }
    }
    return LV_RES_OK;
}

#endif /*LV_USE_MULTI_I18N_LANG*/
